/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "g_local.h"

/*
================
G_Bounce

================
*/
static void G_Bounce(gentity_t * ent, trace_t * trace)
{
	vec3_t          velocity;
	float           dot;
	int             hitTime;
	float           minNormal;
	qboolean        invert = qfalse;

	// reflect the velocity on the trace plane
	hitTime = level.previousTime + (level.time - level.previousTime) * trace->fraction;
	BG_EvaluateTrajectoryDelta(&ent->s.pos, hitTime, velocity);
	dot = DotProduct(velocity, trace->plane.normal);
	VectorMA(velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta);

	if(ent->s.eType == ET_BUILDABLE)
	{
		minNormal = BG_FindMinNormalForBuildable(ent->s.modelindex);
		invert = BG_FindInvertNormalForBuildable(ent->s.modelindex);
	}
	else
		minNormal = 0.707f;

	// cut the velocity to keep from bouncing forever
	if((trace->plane.normal[2] >= minNormal ||
		(invert && trace->plane.normal[2] <= -minNormal)) && trace->entityNum == ENTITYNUM_WORLD)
		VectorScale(ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta);
	else
		VectorScale(ent->s.pos.trDelta, 0.3f, ent->s.pos.trDelta);

	if(VectorLength(ent->s.pos.trDelta) < 10)
	{
		VectorMA(trace->endpos, 0.5, trace->plane.normal, trace->endpos);	// make sure it is off ground
		SnapVector(trace->endpos);
		G_SetOrigin(ent, trace->endpos);
		ent->s.groundEntityNum = trace->entityNum;
		VectorCopy(trace->plane.normal, ent->s.origin2);
		VectorSet(ent->s.pos.trDelta, 0.0f, 0.0f, 0.0f);
		return;
	}

	VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase);
	VectorAdd(ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
	ent->s.pos.trTime = level.time;
}

#define PHYSICS_TIME 200

/*
================
G_Physics

================
*/
void G_Physics(gentity_t * ent, int msec)
{
	vec3_t          origin;
	trace_t         tr;
	int             contents;
	int             mask;

	// if groundentity has been set to -1, it may have been pushed off an edge
	if(ent->s.groundEntityNum == -1)
	{
		if(ent->s.eType == ET_BUILDABLE)
		{
			if(ent->s.pos.trType != BG_FindTrajectoryForBuildable(ent->s.modelindex))
			{
				ent->s.pos.trType = BG_FindTrajectoryForBuildable(ent->s.modelindex);
				ent->s.pos.trTime = level.time;
			}
		}
		else if(ent->s.pos.trType != TR_GRAVITY)
		{
			ent->s.pos.trType = TR_GRAVITY;
			ent->s.pos.trTime = level.time;
		}
	}

	// trace a line from the previous position to the current position
	if(ent->clipmask)
		mask = ent->clipmask;
	else
		mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;	//MASK_SOLID;

	if(ent->s.pos.trType == TR_STATIONARY)
	{
		// check think function
		G_RunThink(ent);

		//check floor infrequently
		if(ent->nextPhysicsTime < level.time)
		{
			VectorCopy(ent->r.currentOrigin, origin);

			VectorMA(origin, -2.0f, ent->s.origin2, origin);

			trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask);

			if(tr.fraction == 1.0f)
				ent->s.groundEntityNum = -1;

			ent->nextPhysicsTime = level.time + PHYSICS_TIME;
		}

		return;
	}

	// get current position
	BG_EvaluateTrajectory(&ent->s.pos, level.time, origin);

	trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask);

	VectorCopy(tr.endpos, ent->r.currentOrigin);

	if(tr.startsolid)
		tr.fraction = 0;

	trap_LinkEntity(ent);		// FIXME: avoid this for stationary?

	// check think function
	G_RunThink(ent);

	if(tr.fraction == 1.0f)
		return;

	// if it is in a nodrop volume, remove it
	contents = trap_PointContents(ent->r.currentOrigin, -1);
	if(contents & CONTENTS_NODROP)
	{
		G_FreeEntity(ent);
		return;
	}

	G_Bounce(ent, &tr);
}
